/* 
 * Help.m, a help object to manage and display RTF help files.
 * The help object owns its own nib section "Help.nib" which has a
 * Help panel - with an NXBrowser to display the help topics, and
 * a scrolling text view to display the help files.  The help files are
 * all stored as RTF text files in a directory called Help within the
 * app wrapper.  At init time, the Help object loads the browser with 
 * names of all the files found in the Help directory.  When a name is
 * chosen from the browser, the help object opens a stream on that file
 * and read the rich text into the text object. The help object also
 * responds to request for context-sensitive help, by trying to find an
 * appropriate help file for the view that was moused down in.
 * This object is a useful addition to any program, because all users
 * appreciate help!
 *
 * Author: Julie Zelenski, NeXT Developer Support
 * You may freely copy, distribute and reuse the code in this example.  
 * NeXT disclaims any warranty of any kind, expressed or implied, as to 
 * its fitness for any particular use.
 */

/* Modified starting 9/21/91 by Subrata K. Sircar */
/* Mostly I've removed functionality. */
 
#import "Help.h"
#import <appkit/appkit.h>
#import <sys/dir.h> //for getdirentries()

#define	GENERAL_HELP_FILE	LocalString("General Help.rtf")

@implementation Help:Object

- init 
{
	LocalHelp(helpDirectory,LocalString("Help"));
    sprintf(noHelpFile,"%s/%s",helpDirectory,GENERAL_HELP_FILE);
    return self;
}

- setHelpBrowser:anObject;
/* Sets the helpBrowser outlet, and calls on the browser to load up.
 */
{
    helpBrowser = anObject;
    [helpBrowser setDelegate:self];
    [helpBrowser loadColumnZero];
    return self;
}

/* TARGET/ACTION METHODS */

- generalHelp:sender;
/* This is the target/action method for the "Help" menu item.  This method 
 * will show the "general help" file.
 */
{
    [self showHelpFile:GENERAL_HELP_FILE];
    return self;
}

- browserHit:sender
/* This is the target/action method from the help topics browser.  When
 * a help topic is selected, this method will show the help file for that
 * topic.
 */
{   
    [self showHelpFile:[[[sender matrixInColumn:0] selectedCell] stringValue]];
    return self;
}


- print:sender;
/* This method is called by the Print menu cell in the main menu.  It will 
 * print the current help file.
 */
{
    [[helpScrollView docView] printPSCode:sender];
    return self;
}

- showHelpFile:(const char *)filename;
/* Tries to open a stream for the specified file in the Help 
 * directory so the text object can readRichText.  Also selects the
 * filename in the browser of help topics.  If the filename doesn't exist,
 * it will select and display the "no help" file.  It also brings the
 * help panel to the front.
 */
{
   NXStream *stream;
   char helpFile[MAXPATHLEN+1];
   static NXPoint origin = {0.0,0.0};

    if (![self browser:helpBrowser selectCell:filename inColumn:0])
        [self browser:helpBrowser selectCell:GENERAL_HELP_FILE inColumn:0];
    sprintf(helpFile,"%s/%s",helpDirectory,filename);
    if ((stream = NXMapFile(helpFile,NX_READONLY)) == NULL)
        stream = NXMapFile(noHelpFile,NX_READONLY);
    if (stream != NULL) {
    	[helpPanel disableFlushWindow];
        [[helpScrollView docView] readRichText:stream]; 
		[[helpScrollView docView] scrollPoint:&origin];
		[[helpPanel reenableFlushWindow] flushWindow];
    	NXCloseMemory(stream,NX_FREEBUFFER);
    }
    [helpPanel orderFront:self];
    return self;
}


static BOOL isOk(const char *s)
/* checks to make sure the filename is not NULL and to verify that it is
 * not a "dot"--hidden file.
 */
{
    return (!s[0] || s[0] == '.') ? NO : YES;
}

static int caseInsensitiveCompare(void *arg1, void *arg2)
/* Compares the two arguments without regard for case using strcasecmp().
*/
{
    char *string1, *string2;

    string1 = *((char **)arg1);
    string2 = *((char **)arg2);
    return strcasecmp(string1,string2);
}

static char **fileList;

- (int)browser:sender fillMatrix:matrix inColumn:(int)column
/* This delegate method goes out to the help directory and gets a list
 * of all the files in that directory.  It creates a list of file names
 * for the static variable fileList, and will load the filenames into the 
 * browser on demand (lazy loading).
 */
{
    long basep;
    char *buf;
    struct direct *dp;
    char **list = NULL;
    int cc, fd, fileCount = 0;
    char dirbuf[8192];

    if ((fd = open(helpDirectory, O_RDONLY, 0644)) > 0) {
		cc = getdirentries(fd, (buf = dirbuf), 8192, &basep);
		while (cc) {
			dp = (struct direct *)buf;
			if (isOk(dp->d_name)) {
				list = addFile(dp->d_name, list, fileCount++, MyZone);
			}
			buf += dp->d_reclen;
			if (buf >= dirbuf + cc) {
				cc = getdirentries(fd, (buf = dirbuf), 8192, &basep);
			}
		}
		close(fd);
		if (list) qsort(list,fileCount,sizeof(char *),caseInsensitiveCompare);
    }
    freeList(fileList);
    fileList = list;
    return fileCount;
}

- browser:sender loadCell:cell atRow:(int)row inColumn:(int)column
/* This delegate method loads the cell for a given row.  The stringValue
 * for that row comes from the fileList.
 */
{
    if (fileList) {
		id originalFont = [cell font];
		id useFont = [Font newFont:LocalHelpFont() size:[originalFont pointSize]];
		if (useFont) {
			[cell setFont:useFont];
			[originalFont free];
		}
		[cell setStringValueNoCopy:fileList[row]];
		[cell setLeaf:YES];
    }
    return self;
}


- (BOOL)browser:sender selectCell:(const char *)title inColumn:(int)column
/* This delegate method selects the cell with the given title.  If it finds
 * a cell with that title, it verifies that it has a file entry in the 
 * fileList, forces the loading of the cell, selects it (highlights) and
 * scrolls the browser so the cell is visible.  It returns a boolean value
 * which indicates whether the cell was found.
 */
{
    int row;
    id matrix;

    if (title) {
		matrix = [sender matrixInColumn:column];
		if (!fileList) return NO;
		for (row = [matrix cellCount]-1; row >= 0; row--) {
	    	if (fileList[row] && !strcmp(title, fileList[row])) {
				[sender getLoadedCellAtRow:row inColumn:column];
				[matrix selectCellAt:row :0];
				[matrix scrollCellToVisible:row :0];
				return YES;
	    	}
		}
    }
    return NO;
}


/* WINDOW DELEGATE METHODS */

- windowWillResize:sender toSize:(NXSize *)frameSize;
/* This method constrains the Help Panel to a reasonable minimum size
 * when the user resizes the panel.
 */
{
    frameSize->width = MAX(frameSize->width,400.0);
    frameSize->height = MAX(frameSize->height,350.0);
    return self;
}


@end